package pers.dzk.fivelnarow.window;

import pers.dzk.fivelnarow.checkerboard.CheckerboardSuper;
import pers.dzk.fivelnarow.config.MainWindowConfig;
import pers.dzk.fivelnarow.config.util.ResourcesPath;
import pers.dzk.fivelnarow.ending.IEnding;
import pers.dzk.fivelnarow.judge.JudgeSuper;
import pers.dzk.fivelnarow.piece.Piece;
import pers.dzk.fivelnarow.piece.PieceType;
import pers.dzk.fivelnarow.player.computer.ComputerPlayer;
import pers.dzk.fivelnarow.player.computer.EvaluateSystem;
import pers.dzk.fivelnarow.player.people.PeoplePlayer;
import pers.dzk.fivelnarow.player.Player;
import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.ImageObserver;
import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 游戏主窗体
 * @author 丁泽凯
 */

public class MainWindow extends JFrame {
    public static List<String> debug = new ArrayList<>();
    private static Point origin = new Point();
    private static IEnding ending;
    private static Player player1;
    private static Player player2;
    private static CheckerboardSuper checkerboard;
    private static JudgeSuper judge;
    private static Clip bgm;
    private static File effectFile = new File(ResourcesPath.AUDIO_PATH+"/effect.wav");
    private static List<Clip> effect = new ArrayList();
    public static Point getOrigin() {
        return origin;
    }

    public static void setOrigin(Point origin) {
        MainWindow.origin = origin;
    }

    public static IEnding getEnding() {
        return ending;
    }

    public static void setEnding(IEnding ending) {
        MainWindow.ending = ending;
    }

    public static Player getPlayer1() {
        return player1;
    }

    public static void setPlayer1(Player player1) {
        MainWindow.player1 = player1;
    }

    public static Player getPlayer2() {
        return player2;
    }

    public static void setPlayer2(Player player2) {
        MainWindow.player2 = player2;
    }

    public static CheckerboardSuper getCheckerboard() {
        return checkerboard;
    }

    public static void setCheckerboard(CheckerboardSuper checkerboard) {
        MainWindow.checkerboard = checkerboard;
    }

    public static JudgeSuper getJudge() {
        return judge;
    }

    public static void setJudge(JudgeSuper judge) {
        MainWindow.judge = judge;
    }

    public static Clip getBgm() {
        return bgm;
    }

    public static void setBgm(Clip bgm) {
        MainWindow.bgm = bgm;
    }

    public static File getEffectFile() {
        return effectFile;
    }

    public static void setEffectFile(File effectFile) {
        MainWindow.effectFile = effectFile;
    }

    public static List<Clip> getEffect() {
        return effect;
    }

    public static void setEffect(List<Clip> effect) {
        MainWindow.effect = effect;
    }

    /**
     * 局域网对战
     * @param checkerboard 棋盘
     * @param player1 玩家
     * @param player2 玩家
     * @param socket 网线
     * @param judge 裁判
     * @param ending 结局
     */
    public MainWindow(CheckerboardSuper checkerboard, PeoplePlayer player1, PeoplePlayer player2,Socket socket, JudgeSuper judge, IEnding ending){
        //数据同步思路：当一方触发事件后把所有参数打包发给对方，对方接收后把原数据覆盖掉然后立马调用一次jpanel的paint方法
        //连接思路：局域网可选择新建房间（窗口显示自己ip地址）或者连接对方(窗口提示你输入对方ip地址)
        System.out.println("你是"+player1.getPieceType()+"方");
        System.out.println("是否先手"+player1.isMyTime());
        ImageIcon icon = new ImageIcon(ResourcesPath.IMG_PATH+"/startWindow/logo.jpg");
        JLabel label = new JLabel("你是"+player1.getPieceType()+"方,"+(player1.isMyTime() ? "先手":"后手"));
        label.setFont(new Font("",Font.TRUETYPE_FONT,20));
        JOptionPane.showMessageDialog(null, label, "提示", JOptionPane.INFORMATION_MESSAGE,icon);
        Thread receiveMessage = new Thread(() -> {
            while (true) {
                //接电话
                try {
                    InputStream os = socket.getInputStream();
                    ObjectInputStream ois = new ObjectInputStream(os);
                    //替换数据
                    Map<String,Object> map = (Map<String, Object>) ois.readObject();
                    GameState gs = (GameState) map.get("GameState");
                    if(gs != null){
                        judge.setGameState(gs);
                    }else{
                        PeoplePlayer player1_copy = (PeoplePlayer) map.get("player1");
                        player1.setMyTime(player1_copy.isMyTime());
                        player1.setPieceList(player1_copy.getPieceList());
                        PeoplePlayer player2_copy = (PeoplePlayer) map.get("player2");
                        player2.setMyTime(player2_copy.isMyTime());
                        player2.setPieceList(player2_copy.getPieceList());
                        JudgeSuper judge_copy = (JudgeSuper) map.get("judge");
                        judge.setGameState(judge_copy.getGameState());
                        judge.setValidTime(judge_copy.isValidTime());
                        judge.setOver(judge_copy.isOver());
                        judge.setTimeCount(judge_copy.getTimeCount());
                        judge.setFailPlayer(judge_copy.getFailPlayer());
                        judge.setVictoryPlayer(judge_copy.getVictoryPlayer());
                        CheckerboardSuper checkerboard_copy = (CheckerboardSuper) map.put("checkerboard",checkerboard);
                        checkerboard.setPieceMatrix(checkerboard_copy.getPieceMatrix());
                        checkerboard.setPointMatrix(checkerboard_copy.getPointMatrix());
                    }
                    repaint();
                } catch (IOException | ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        receiveMessage.start();
        initial(checkerboard,player1,player2,ending,judge,new MyMouseListener(){
            private long pressedTime;

            private void sendMessage(){
                //打电话
                try {
                    OutputStream os = socket.getOutputStream();
                    ObjectOutputStream ous = new ObjectOutputStream(os);
                    //发送数据
                    Map<String,Object> map = new HashMap();
                    map.put("player1",player2);
                    map.put("player2",player1);
                    map.put("judge",judge);
                    map.put("checkerboard",checkerboard);
                    ous.writeObject(map);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            @Override
            public void mouseReleased(MouseEvent e) {

                if(MainWindow.judge.getGameState() == GameState.运行中){
                    long time = System.currentTimeMillis();
                    if(time - pressedTime  < 500){
                        System.out.println("####################第"+ (1+MainWindow.judge.getTimeCount()) +"回合####################");
                        if(player1.isMyTime()){
                            if(add(player1,new Point(e.getX(),e.getY()))){
                                judge.setValidTime(true);
                                judge.setTimeCount(judge.getTimeCount()+1);
                                player1.setMyTime(!player1.isMyTime());
                                player2.setMyTime(!player2.isMyTime());
                                effectStart();
                                repaint();
                                //发数据包
                                sendMessage();
                            }
                        }else{
                            ImageIcon icon = new ImageIcon(ResourcesPath.IMG_PATH+"/startWindow/logo.jpg");
                            JLabel label = new JLabel("不是你的回合！");
                            label.setFont(new Font("",Font.TRUETYPE_FONT,20));
                            JOptionPane.showMessageDialog(null, label, "提示", JOptionPane.INFORMATION_MESSAGE,icon);
                        }
                    }
                }
            }
            @Override
            public void mousePressed(MouseEvent e){
                pressedTime = System.currentTimeMillis();
            }
        },new MyKeyListener(){
            @Override
            public void keyReleased(KeyEvent e){
                //空格暂停游戏
                judge.setValidTime(false);
                if(e.getKeyCode()==KeyEvent.VK_SPACE){
                    if(judge.getGameState() != GameState.结束){
                        if(judge.getGameState() == GameState.运行中){
                            judge.setGameState(GameState.暂停);
                            bgm.stop();
                            repaint();
                        }else{
                            bgm.start();
                            judge.setGameState(GameState.运行中);
                            repaint();
                        }
                        try {
                            OutputStream os = socket.getOutputStream();
                            ObjectOutputStream oos = new ObjectOutputStream(os);
                            Map<String,GameState> map = new HashMap();
                            judge.getGameState();
                            map.put("GameState",judge.getGameState());
                            oos.writeObject(map);
                        } catch (IOException ex) {
                            throw new RuntimeException(ex);
                        }
                    }
                } else if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
                    ImageIcon icon = new ImageIcon(ResourcesPath.IMG_PATH+"/startWindow/logo.jpg");
                    JLabel label = new JLabel("确认退出游戏吗？");
                    label.setFont(new Font("",Font.TRUETYPE_FONT,20));
                    if(JOptionPane.showConfirmDialog(null, label, "提示", JOptionPane.OK_CANCEL_OPTION,JOptionPane.QUESTION_MESSAGE,icon) == 0){
                        System.exit(1);
                    }
                }else if(e.getKeyCode() == KeyEvent.VK_E){
                    if(bgm.isRunning()){
                        bgm.stop();
                    }else {
                        bgm.start();
                    }
                }
            }
        });

    }

    /**
     * 双人对战
     * @param checkerboard 棋盘
     * @param player1 玩家
     * @param player2 玩家
     * @param judge 裁判
     * @param ending 结局
     */
    public MainWindow(CheckerboardSuper checkerboard, PeoplePlayer player1, PeoplePlayer player2, JudgeSuper judge, IEnding ending){
        initial(checkerboard,player1,player2,ending,judge,new MyMouseListener(){
            private long pressedTime;
            @Override
            public void mouseReleased(MouseEvent e) {
                if(judge.getGameState() == GameState.运行中){
                    long time = System.currentTimeMillis();
                    if(time -pressedTime < 500){
                        System.out.println("####################第"+ (1+judge.getTimeCount()) +"回合####################");
                        if(add(player1.isMyTime() ? player1 : player2,new Point(e.getX(),e.getY()))){
                            judge.setValidTime(true);
                            judge.setTimeCount(judge.getTimeCount()+1);
                            player1.setMyTime(!player1.isMyTime());
                            player2.setMyTime(!player2.isMyTime());
                            effectStart();
                            repaint();
                        }
                    }
                }
            }
            @Override
            public void mousePressed(MouseEvent e){
                pressedTime = System.currentTimeMillis();

            }
        },new MyKeyListener());
    }
    /**
     * 机器人对战
     * @param checkerboard 棋盘
     * @param player1 玩家
     * @param player2 玩家
     * @param judge 裁判
     * @param ending 结局
     */
    public MainWindow(CheckerboardSuper checkerboard, ComputerPlayer player1, ComputerPlayer player2, JudgeSuper judge, IEnding ending){
        initial(checkerboard,player1,player2,ending,judge,new MyMouseListener(),new MyKeyListener());
        Thread t = new Thread(() -> {
            while (judge.getFailPlayer() == null){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                Piece p = (player1.isMyTime() ? player1 : player2).ai();
                Point index = correct(p.getPoint());
                checkerboard.getPieceMatrix()[index.y][index.x] = p;
                judge.setValidTime(true);
                judge.setTimeCount(judge.getTimeCount()+1);
                judge.execute();
                effectStart();
                repaint();
                player1.setMyTime(!player1.isMyTime());
                player2.setMyTime(!player2.isMyTime());
            }
        });
        t.start();
    }
    /**
     * 人机对战
     * @param checkerboard 棋盘
     * @param player1 玩家
     * @param player2 玩家
     * @param judge 裁判
     * @param ending 结局
     */
    public MainWindow(CheckerboardSuper checkerboard, PeoplePlayer player1, ComputerPlayer player2, JudgeSuper judge, IEnding ending){
        initial(checkerboard,player1,player2,ending,judge,new MyMouseListener(){
            private long pressedTime;
            @Override
            public void mouseReleased(MouseEvent e) {
                if(judge.getGameState() == GameState.运行中){
                    long time = System.currentTimeMillis();
                    if(time -pressedTime < 500){
                        System.out.println("####################第"+ (1+judge.getTimeCount()) +"回合####################");
                        if(player1.isMyTime()){
                            add(player1,new Point(e.getX(),e.getY()));
                            judge.setValidTime(true);
                            judge.setTimeCount(judge.getTimeCount()+1);
                            effectStart();
                            repaint();
                            player1.setMyTime(!player1.isMyTime());
                            player2.setMyTime(!player2.isMyTime());
                        }
                    }
                }
            }
            @Override
            public void mousePressed(MouseEvent e){
                pressedTime = System.currentTimeMillis();

            }
        },new MyKeyListener());
        //人机线程
        Thread computer = new Thread(() -> {
            while (true){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if(player2.isMyTime() && GameState.运行中 == judge.getGameState()){
                    System.out.println("####################第"+ (1+judge.getTimeCount()) +"回合####################");
                    Piece p = player2.ai();
                    Point index = correct(p.getPoint());
                    checkerboard.getPieceMatrix()[index.y][index.x] = p;
                    judge.setValidTime(true);
                    judge.setTimeCount(judge.getTimeCount()+1);
                    player1.setMyTime(!player1.isMyTime());
                    player2.setMyTime(!player2.isMyTime());
                    effectStart();
                    repaint();
                }
            }
        });
        computer.start();
    }

    /**
     * 初始化
     * @param checkerboard
     */
    private void initial(CheckerboardSuper checkerboard, Player player1, Player player2, IEnding ending, JudgeSuper judge, MouseListener mouseListener,KeyListener keyListener){
        MainWindow.checkerboard = checkerboard;
        MainWindow.player1 = player1;
        MainWindow.player2 = player2;
        MainWindow.ending = ending;
        MainWindow.judge = judge;
        effectInit();
        bgmInit();
        //标题
        setTitle("五子棋");
        //取消边框
        setUndecorated(true);
        //窗体大小
        setSize(MainWindowConfig.getWidth(), MainWindowConfig.getHeight());
        //居中
        setLocationRelativeTo(null);
        //固定大小
        setResizable(false);
        //允许自定义画板大小
        setLayout(new FlowLayout(0,0,0));
        //添加画板,setContentPane替代add会导致画板无法自定义
        MyPanel myPanel = new MyPanel(checkerboard,ending,judge);
        add(myPanel);
        //可见
        setVisible(true);
        //窗体可拖动
        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                origin.x = e.getX();
                origin.y = e.getY();
            }
        });
        addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseDragged(MouseEvent e) {
                judge.setValidTime(false);
                Point p = getLocation();
                int a = Math.abs((origin.x-e.getX()));
                int b = Math.abs((origin.y-e.getY()));
                //防误移动
                if(a + b > 100){
                    setLocation(p.x + e.getX() - origin.x, p.y + e.getY() - origin.y);
                }
            }
        });
        //添加鼠标,键盘监听器
        addMouseListener(mouseListener);
        addKeyListener(keyListener);
    }
    private class MyKeyListener implements KeyListener {
        @Override
        public void keyTyped(KeyEvent e) {}
        @Override
        public void keyPressed(KeyEvent e) {}
        @Override
        public void keyReleased(KeyEvent e) {
            //空格暂停游戏
            judge.setValidTime(false);
            if(e.getKeyCode()==KeyEvent.VK_SPACE){
                if(judge.getGameState() != GameState.结束){
                    if(judge.getGameState() == GameState.运行中){
                        judge.setGameState(GameState.暂停);
                        bgm.stop();
                        repaint();
                    }else{
                        bgm.start();
                        judge.setGameState(GameState.运行中);
                        repaint();
                    }
                }
            } else if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
                ImageIcon icon = new ImageIcon(ResourcesPath.IMG_PATH+"/startWindow/logo.jpg");
                JLabel label = new JLabel("确认退出游戏吗？");
                label.setFont(new Font("",Font.TRUETYPE_FONT,20));
                if(JOptionPane.showConfirmDialog(null, label, "提示", JOptionPane.OK_CANCEL_OPTION,JOptionPane.QUESTION_MESSAGE,icon) == 0){
                    System.exit(1);
                }
            }else if(e.getKeyCode() == KeyEvent.VK_E){
                if(bgm.isRunning()){
                    bgm.stop();
                }else {
                    bgm.start();
                }
            }
        }
    }

    /**
     * bgm初始化
     */
    private void bgmInit() {
//        try {
//            bgm = AudioSystem.getClip();
//            AudioInputStream aisBgm = AudioSystem.getAudioInputStream(new File(ResourcesPath.AUDIO_PATH+"/bgm.wav"));
//            bgm.open(aisBgm);
//        } catch (LineUnavailableException | UnsupportedAudioFileException | IOException e) {
//            e.printStackTrace();
//        }
//        bgm.start();
//        //循环播放
//        bgm.loop(Clip.LOOP_CONTINUOUSLY);
    }

    /**
     * 下棋音效初始化
     */
    private void effectInit(){
//        try {
//            for(int i = 0; i <player1.getCount()+player2.getCount();i++){
//                try {
//                    Clip clip = AudioSystem.getClip();
//                    clip.open( AudioSystem.getAudioInputStream(effectFile));
//                    effect.add(clip);
//                } catch (Exception e) {
//                    throw new RuntimeException(e);
//                }
//            }
//        } catch (RuntimeException e) {
//            e.printStackTrace();
//        }
    }
    private void effectStart(){
//        effect.get(effect.size()-1).start();
//        effect.remove(effect.size()-1);

    }

    /**
     * 查找距离实际指针位置最近的可用点并返回它与INDEX对应的下标
     * @param point 鼠标指针实际位置
     * @return INDEX下标
     */
    private Point correct(Point point) {
        //搜索最近的可用点位
        int min = Math.abs(getHeight())+Math.abs(getWidth()),x = 0,y = 0;
        for (int i = 0; i < checkerboard.getPointMatrix().length; i++){
            for (int j = 0; j < checkerboard.getPointMatrix().length; j++){
                Point difference = new Point(checkerboard.getPointMatrix()[i][j].x-point.x, checkerboard.getPointMatrix()[i][j].y-point.y);
                int xy = Math.abs(difference.x)+Math.abs(difference.y);
                if(xy < min){
                    min = xy;
                    y = i;
                    x = j;
                }
            }
        }
        //检查点位是否被占用
        if(checkerboard.getPieceMatrix()[y][x].getCode() != PieceType.空){
            ImageIcon icon = new ImageIcon(ResourcesPath.IMG_PATH+"/startWindow/logo.jpg");
            System.out.println("该回合无效，坐标点已被棋子占用！");
            JLabel label = new JLabel("妾身做不到啊QAQ~");
            label.setFont(new Font("",Font.TRUETYPE_FONT,20));
            JOptionPane.showMessageDialog(null,label,"提示",JOptionPane.INFORMATION_MESSAGE,icon);
            return null;
        }
        return new Point(x,y);
    }

    /**
     * 把玩家下的棋子添加到棋盘里去
     * @param player
     * @param point
     * @return
     */
    private boolean add(PeoplePlayer player, Point point){
        System.out.println(player.getName()+"的下棋坐标点(x,y): ("+(int)point.getX()+","+(int)point.getY()+")");
        Point index = correct(point);
        if(index != null) {
            MainWindow.debug.add(index.x+","+index.y);
            for (int i = 0;i < MainWindow.debug.size();i++) {
                System.out.println(i+". "+"("+MainWindow.debug.get(i)+")");
            }
            EvaluateSystem.getEvaluate().update(index.x,index.y,player.getPieceType());
            checkerboard.getPieceMatrix()[index.y][index.x] = player.play(new Point(checkerboard.getPointMatrix()[index.y][index.x]));
            System.out.println("矫正后的位置："+checkerboard.getPieceMatrix()[index.y][index.x].getPoint());
            return true;
        }
        return false;
    }

    /**
     * 内置MouseListener
     */
    private class MyMouseListener implements MouseListener{
        @Override
        public void mouseClicked(MouseEvent e) {}
        @Override
        public void mousePressed(MouseEvent e) {}
        @Override
        public void mouseReleased(MouseEvent e) {}
        @Override
        public void mouseEntered(MouseEvent e) {}
        @Override
        public void mouseExited(MouseEvent e) {}
    }

    private static class MyPanel extends JPanel{
        private CheckerboardSuper checkerboard;
        private IEnding ending;
        private JudgeSuper judge;
        public MyPanel(CheckerboardSuper checkerboard, IEnding ending, JudgeSuper judge){
            this.checkerboard = checkerboard;
            this.ending = ending;
            this.judge = judge;
            checkerboard.initial(this);
        }
        @Override
        public void paint(Graphics g){
            super.paint(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            checkerboard.paintCheckerboard(g2);
            paintCount(g2);
            //注意：窗体移动时会自动调用paint方法
            judge.execute();
            ending.paintEnding(g2);
            ending.handle();
            paintStop(g2,this);
        }

        public void paintCount(Graphics2D g2d) {
            g2d.setColor(Color.black);
            Font font = new Font("",Font.BOLD,22);
            g2d.setFont(font);
            Player bp = getPlayer1().getPieceType() == PieceType.黑棋 ? getPlayer1() : getPlayer2();
            Player wp = getPlayer1().getPieceType() == PieceType.白棋 ? getPlayer1() : getPlayer2();
            g2d.drawString("黑棋剩余棋子 "+bp.getCount(),50,50);
            g2d.drawString("白棋剩余棋子 "+wp.getCount(),250,50);
            g2d.drawString("第"+(getJudge().getTimeCount()+1)+"回合",450,50);
        }

        public void paintStop(Graphics2D g2d, ImageObserver observer) {
            if(judge.getGameState() == GameState.暂停){
                Image image = new ImageIcon(ResourcesPath.IMG_PATH+"/mainWindow/stop.png").getImage();
                g2d.drawImage(image, MainWindowConfig.getWidth()/2-80, MainWindowConfig.getHeight()/2-100,observer);
            }
        }
    }


}
